 /*******************************************************************************
  * Copyright (c) 2000, 2006 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/epl-v10.html
  *
  * Contributors:
  * IBM Corporation - initial API and implementation
  * Sebastian Davids <sdavids@gmx.de> - Fix for bug 19346 - Dialog
  * font should be activated and used by other components.
  *******************************************************************************/
 package org.eclipse.ui.dialogs;

 import org.eclipse.core.runtime.Assert;
 import org.eclipse.core.runtime.IStatus;
 import org.eclipse.core.runtime.Status;
 import org.eclipse.jface.dialogs.IDialogConstants;
 import org.eclipse.jface.viewers.ILabelProvider;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.custom.BusyIndicator;
 import org.eclipse.swt.events.KeyEvent;
 import org.eclipse.swt.events.KeyListener;
 import org.eclipse.swt.events.SelectionEvent;
 import org.eclipse.swt.events.SelectionListener;
 import org.eclipse.swt.layout.GridData;
 import org.eclipse.swt.widgets.Button;
 import org.eclipse.swt.widgets.Composite;
 import org.eclipse.swt.widgets.Event;
 import org.eclipse.swt.widgets.Label;
 import org.eclipse.swt.widgets.Listener;
 import org.eclipse.swt.widgets.Shell;
 import org.eclipse.swt.widgets.Text;
 import org.eclipse.ui.PlatformUI;

 /**
  * An abstract class to select elements out of a list of elements.
  *
  * @since 2.0
  */
 public abstract class AbstractElementListSelectionDialog extends
         SelectionStatusDialog {

     private ILabelProvider fRenderer;

     private boolean fIgnoreCase = true;

     private boolean fIsMultipleSelection = false;

     private boolean fMatchEmptyString = true;

     private boolean fAllowDuplicates = true;

     private Label fMessage;

     protected FilteredList fFilteredList;

     private Text fFilterText;

     private ISelectionStatusValidator fValidator;

     private String fFilter = null;

     private String fEmptyListMessage = ""; //$NON-NLS-1$

     private String fEmptySelectionMessage = ""; //$NON-NLS-1$

     private int fWidth = 60;

     private int fHeight = 18;

     private Object [] fSelection = new Object [0];

     /**
      * Constructs a list selection dialog.
      * @param parent The parent for the list.
      * @param renderer ILabelProvider for the list
      */
     protected AbstractElementListSelectionDialog(Shell parent,
             ILabelProvider renderer) {
         super(parent);
         fRenderer = renderer;

         int shellStyle = getShellStyle();
         setShellStyle(shellStyle | SWT.MAX | SWT.RESIZE);
     }

     /**
      * Handles default selection (double click).
      * By default, the OK button is pressed.
      */
     protected void handleDefaultSelected() {
         if (validateCurrentSelection()) {
             buttonPressed(IDialogConstants.OK_ID);
         }
     }

     /**
      * Specifies if sorting, filtering and folding is case sensitive.
      * @param ignoreCase
      */
     public void setIgnoreCase(boolean ignoreCase) {
         fIgnoreCase = ignoreCase;
     }

     /**
      * Returns if sorting, filtering and folding is case sensitive.
      * @return boolean
      */
     public boolean isCaseIgnored() {
         return fIgnoreCase;
     }

     /**
      * Specifies whether everything or nothing should be filtered on
      * empty filter string.
      * @param matchEmptyString boolean
      */
     public void setMatchEmptyString(boolean matchEmptyString) {
         fMatchEmptyString = matchEmptyString;
     }

     /**
      * Specifies if multiple selection is allowed.
      * @param multipleSelection
      */
     public void setMultipleSelection(boolean multipleSelection) {
         fIsMultipleSelection = multipleSelection;
     }

     /**
      * Specifies whether duplicate entries are displayed or not.
      * @param allowDuplicates
      */
     public void setAllowDuplicates(boolean allowDuplicates) {
         fAllowDuplicates = allowDuplicates;
     }

     /**
      * Sets the list size in unit of characters.
      * @param width the width of the list.
      * @param height the height of the list.
      */
     public void setSize(int width, int height) {
         fWidth = width;
         fHeight = height;
     }

     /**
      * Sets the message to be displayed if the list is empty.
      * @param message the message to be displayed.
      */
     public void setEmptyListMessage(String message) {
         fEmptyListMessage = message;
     }

     /**
      * Sets the message to be displayed if the selection is empty.
      * @param message the message to be displayed.
      */
     public void setEmptySelectionMessage(String message) {
         fEmptySelectionMessage = message;
     }

     /**
      * Sets an optional validator to check if the selection is valid.
      * The validator is invoked whenever the selection changes.
      * @param validator the validator to validate the selection.
      */
     public void setValidator(ISelectionStatusValidator validator) {
         fValidator = validator;
     }

     /**
      * Sets the elements of the list (widget).
      * To be called within open().
      * @param elements the elements of the list.
      */
     protected void setListElements(Object [] elements) {
         Assert.isNotNull(fFilteredList);
         fFilteredList.setElements(elements);
     }

     /**
      * Sets the filter pattern.
      * @param filter the filter pattern.
      */
     public void setFilter(String filter) {
         if (fFilterText == null) {
             fFilter = filter;
         } else {
             fFilterText.setText(filter);
         }
     }

     /**
      * Returns the current filter pattern.
      * @return returns the current filter pattern or <code>null<code> if filter was not set.
      */
     public String getFilter() {
         if (fFilteredList == null) {
             return fFilter;
         } else {
             return fFilteredList.getFilter();
         }
     }

     /**
      * Returns the indices referring the current selection.
      * To be called within open().
      * @return returns the indices of the current selection.
      */
     protected int[] getSelectionIndices() {
         Assert.isNotNull(fFilteredList);
         return fFilteredList.getSelectionIndices();
     }

     /**
      * Returns an index referring the first current selection.
      * To be called within open().
      * @return returns the indices of the current selection.
      */
     protected int getSelectionIndex() {
         Assert.isNotNull(fFilteredList);
         return fFilteredList.getSelectionIndex();
     }

     /**
      * Sets the selection referenced by an array of elements.
      * Empty or null array removes selection.
      * To be called within open().
      * @param selection the indices of the selection.
      */
     protected void setSelection(Object [] selection) {
         Assert.isNotNull(fFilteredList);
         fFilteredList.setSelection(selection);
     }

     /**
      * Returns an array of the currently selected elements.
      * To be called within or after open().
      * @return returns an array of the currently selected elements.
      */
     protected Object [] getSelectedElements() {
         Assert.isNotNull(fFilteredList);
         return fFilteredList.getSelection();
     }

     /**
      * Returns all elements which are folded together to one entry in the list.
      * @param index the index selecting the entry in the list.
      * @return returns an array of elements folded together.
      */
     public Object [] getFoldedElements(int index) {
         Assert.isNotNull(fFilteredList);
         return fFilteredList.getFoldedElements(index);
     }

     /**
      * Creates the message text widget and sets layout data.
      * @param composite the parent composite of the message area.
      */
     protected Label createMessageArea(Composite composite) {
         Label label = super.createMessageArea(composite);

         GridData data = new GridData();
         data.grabExcessVerticalSpace = false;
         data.grabExcessHorizontalSpace = true;
         data.horizontalAlignment = GridData.FILL;
         data.verticalAlignment = GridData.BEGINNING;
         label.setLayoutData(data);

         fMessage = label;

         return label;
     }

     /**
      * Handles a selection changed event.
      * By default, the current selection is validated.
      */
     protected void handleSelectionChanged() {
         validateCurrentSelection();
     }

     /**
      * Validates the current selection and updates the status line
      * accordingly.
      * @return boolean <code>true</code> if the current selection is
      * valid.
      */
     protected boolean validateCurrentSelection() {
         Assert.isNotNull(fFilteredList);

         IStatus status;
         Object [] elements = getSelectedElements();

         if (elements.length > 0) {
             if (fValidator != null) {
                 status = fValidator.validate(elements);
             } else {
                 status = new Status(IStatus.OK, PlatformUI.PLUGIN_ID,
                         IStatus.OK, "", //$NON-NLS-1$
 null);
             }
         } else {
             if (fFilteredList.isEmpty()) {
                 status = new Status(IStatus.ERROR, PlatformUI.PLUGIN_ID,
                         IStatus.ERROR, fEmptyListMessage, null);
             } else {
                 status = new Status(IStatus.ERROR, PlatformUI.PLUGIN_ID,
                         IStatus.ERROR, fEmptySelectionMessage, null);
             }
         }

         updateStatus(status);

         return status.isOK();
     }

     /*
      * @see Dialog#cancelPressed
      */
     protected void cancelPressed() {
         setResult(null);
         super.cancelPressed();
     }

     /**
      * Creates a filtered list.
      * @param parent the parent composite.
      * @return returns the filtered list widget.
      */
     protected FilteredList createFilteredList(Composite parent) {
         int flags = SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL
                 | (fIsMultipleSelection ? SWT.MULTI : SWT.SINGLE);

         FilteredList list = new FilteredList(parent, flags, fRenderer,
                 fIgnoreCase, fAllowDuplicates, fMatchEmptyString);

         GridData data = new GridData();
         data.widthHint = convertWidthInCharsToPixels(fWidth);
         data.heightHint = convertHeightInCharsToPixels(fHeight);
         data.grabExcessVerticalSpace = true;
         data.grabExcessHorizontalSpace = true;
         data.horizontalAlignment = GridData.FILL;
         data.verticalAlignment = GridData.FILL;
         list.setLayoutData(data);
         list.setFont(parent.getFont());
         list.setFilter((fFilter == null ? "" : fFilter)); //$NON-NLS-1$

         list.addSelectionListener(new SelectionListener() {
             public void widgetDefaultSelected(SelectionEvent e) {
                 handleDefaultSelected();
             }

             public void widgetSelected(SelectionEvent e) {
                 handleWidgetSelected();
             }
         });

         fFilteredList = list;

         return list;
     }

     // 3515
 private void handleWidgetSelected() {
         Object [] newSelection = fFilteredList.getSelection();

         if (newSelection.length != fSelection.length) {
             fSelection = newSelection;
             handleSelectionChanged();
         } else {
             for (int i = 0; i != newSelection.length; i++) {
                 if (!newSelection[i].equals(fSelection[i])) {
                     fSelection = newSelection;
                     handleSelectionChanged();
                     break;
                 }
             }
         }
     }

     protected Text createFilterText(Composite parent) {
         Text text = new Text(parent, SWT.BORDER);

         GridData data = new GridData();
         data.grabExcessVerticalSpace = false;
         data.grabExcessHorizontalSpace = true;
         data.horizontalAlignment = GridData.FILL;
         data.verticalAlignment = GridData.BEGINNING;
         text.setLayoutData(data);
         text.setFont(parent.getFont());

         text.setText((fFilter == null ? "" : fFilter)); //$NON-NLS-1$

         Listener listener = new Listener() {
             public void handleEvent(Event e) {
                 fFilteredList.setFilter(fFilterText.getText());
             }
         };
         text.addListener(SWT.Modify, listener);

         text.addKeyListener(new KeyListener() {
             public void keyPressed(KeyEvent e) {
                 if (e.keyCode == SWT.ARROW_DOWN) {
                     fFilteredList.setFocus();
                 }
             }

             public void keyReleased(KeyEvent e) {
             }
         });

         fFilterText = text;

         return text;
     }

     /*
      * (non-Javadoc)
      * @see org.eclipse.jface.window.Window#open()
      */
     public int open() {
         super.open();
         return getReturnCode();
     }

     private void access$superCreate() {
         super.create();
     }

     /*
      * (non-Javadoc)
      * @see org.eclipse.jface.window.Window#create()
      */
     public void create() {

         BusyIndicator.showWhile(null, new Runnable () {
             public void run() {
                 access$superCreate();

                 Assert.isNotNull(fFilteredList);

                 if (fFilteredList.isEmpty()) {
                     handleEmptyList();
                 } else {
                     validateCurrentSelection();
                     fFilterText.selectAll();
                     fFilterText.setFocus();
                 }
             }
         });

     }

     /**
      * Handles empty list by disabling widgets.
      */
     protected void handleEmptyList() {
         fMessage.setEnabled(false);
         fFilterText.setEnabled(false);
         fFilteredList.setEnabled(false);
         updateOkState();
     }

     /**
      * Update the enablement of the OK button based on whether or not there
      * is a selection.
      *
      */
     protected void updateOkState() {
         Button okButton = getOkButton();
         if (okButton != null) {
             okButton.setEnabled(getSelectedElements().length != 0);
         }
     }
 }

